﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SevenZip.Compression.LZMA;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class DsLoader : Singleton<DsLoader>
{
    public static string nativeUrl;
    public static string strState;

    Dictionary<string, AssetBundle> assetBundles;
    private JObject resJson = new JObject();

    public DsLoader()
    {
        assetBundles = new Dictionary<string, AssetBundle>();
        nativeUrl = Path.Combine(Application.persistentDataPath, @"vercache\");
        //string resStr = DsLoadText("Assets.Resources.Json.assetbundle");
    }

    public void InitResJson()
    {
        AssetBundle bundle = LoadAB("Assets.Resources.Json.assetbundle");
        TextAsset ts = bundle.mainAsset as TextAsset;
        JArray json = JsonConvert.DeserializeObject(ts.text) as JArray;
        for (int i = 0; i < json.Count; ++i)
        {
            JObject token = json[i] as JObject;
            string resName = token["资源名"].ToString();
            try
            {
                resJson.Add(resName, token);
            }
            catch (System.Exception e)
            {
                throw new System.ArgumentException("资源表错误，资源名：" + resName
                    + " token:" + token.ToString() + " Exception:" + e.ToString());
            }
        }
    }

    public AssetBundle DsLoad(string name)
    {
        if (assetBundles.ContainsKey(name)) { return assetBundles[name]; }
        string realName = resJson[name]["AssetBundle"].ToString();
        int depNum = resJson[name]["依赖数量"].JsonToInt();

        for (int idx = 0; idx < depNum; ++idx)
        {
            string str = string.Format("依赖资源{0}", idx + 1);
            string depName = resJson[name][str].ToString();
            DsLoad(depName);
        }
        AssetBundle bundle = LoadAB(realName);
        assetBundles.Add(name, bundle);
        return bundle;
    }

    public AssetBundle LoadAB(string name)
    {
        string nativeResPath = Path.Combine(nativeUrl, name);
        strState = string.Format("Load Res:{0}", nativeResPath);
        return AssetBundle.LoadFromFile(nativeResPath);
    }

    public void ClearAssetBundle()
    {
        if (assetBundles != null && assetBundles.Count > 0)
        {
            foreach (var kv in assetBundles)
            {
                kv.Value.Unload(false);
            }
            assetBundles.Clear();
        }
    }

    public Object DsInstantiate(string name)
    {
        return DsInstantiate(name, Vector3.zero, Quaternion.identity);
    }

    public Object DsInstantiate(string name, Vector3 position, Quaternion rotation)
    {
        string realName = GetRealName(name);
        if (Utility.GetPlatForm() != EPlatform.Editor)
        {
            return Object.Instantiate(Resources.Load(realName));
        }

        Object obj = DsLoad(name).mainAsset;
        GameObject gameObject = Object.Instantiate(obj) as GameObject;
        var transList = gameObject.GetComponentsInChildren<Transform>();

        foreach (Transform t in transList)
        {
            if (t.GetComponent<Renderer>() != null)
            {
                var materials = t.GetComponent<Renderer>().materials;
                for (int idx = 0; idx < materials.Length; ++idx)
                {
                    string shader = materials[idx].shader.name;
                    materials[idx].shader = Shader.Find(shader);
                }
            }
        }
        return gameObject;
    }

    public string DsLoadText(string name)
    {
        string realName = GetRealName(name);
        if (Utility.GetPlatForm() != EPlatform.Editor)
        {
            Object o = Resources.Load(realName);
            return o.ToString();
        }

        AssetBundle bundle = DsLoad(name);
        TextAsset ts = bundle.mainAsset as TextAsset;
        bundle.Unload(false);
        return ts.text;
    }

    private string GetRealName(string name)
    {
        if (Utility.GetPlatForm() != EPlatform.Editor)
        {
            return resJson[name]["Path"].ToString();
        }
        return resJson[name]["AssetBundle"].ToString();
    }


    public static void UnCompressRes(byte[] data, string name)
    {
        string nativeResPath = Path.Combine(nativeUrl, name);
        if (name.Contains("VersionNum") || name.Contains("PackageVersion"))
        {
            string path = name.Contains("assetbundle") ? name : name + ".xml";
            string outPath = Path.Combine(nativeUrl, path);
            FileStream stream = new FileStream(outPath, FileMode.Create);
            stream.Write(data, 0, data.Length);
            stream.Flush(); stream.Close();
        }
        else
        {
            string tempPath = Path.Combine(nativeUrl, "temp");
            FileStream stream = new FileStream(tempPath, FileMode.Create);
            stream.Write(data, 0, data.Length);
            stream.Flush(); stream.Close();
            DecompressFile(tempPath, nativeResPath);
        }
        //strState = string.Format("Down File: {0}", nativeResPath);
    }
    private static void DecompressFile(string inFile, string outFile)
    {
        Decoder coder = new Decoder();
        FileStream input = new FileStream(inFile, FileMode.Open);
        FileStream output = new FileStream(outFile, FileMode.Create);

        // Read the decoder properties
        byte[] properties = new byte[5];
        input.Read(properties, 0, 5);

        // Read in the decompress file size.
        byte[] fileLengthBytes = new byte[8];
        input.Read(fileLengthBytes, 0, 8);
        long fileLength = System.BitConverter.ToInt64(fileLengthBytes, 0);

        // Decompress the file.
        coder.SetDecoderProperties(properties);
        coder.Code(input, output, input.Length, fileLength, null);
        output.Flush(); output.Close(); input.Close();
    }
}